package com.susallwave.srcb.aspect;

import cn.dev33.satoken.stp.StpUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.susallwave.srcb.entity.sys.OperationLog;
import com.susallwave.srcb.exception.BusinessException;
import com.susallwave.srcb.service.sys.OperationLogService;
import com.susallwave.srcb.utils.IpUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;

@Slf4j
@Aspect
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class OperationLogAspect {

    private final OperationLogService operationLogService;

    private final ObjectMapper jacksonMapper; // 使用Jackson

    @Pointcut("execution(* com.susallwave.srcb.controller..*.*(..))")
    public void controllerPointcut() {}

    /***
     * 配合SaTokenConfig类
     * 在登陆前不会被记录日志(排除接口会记录,无论是否登陆)
     * @return
     */
    @Around("controllerPointcut()")
    public Object logAround(ProceedingJoinPoint joinPoint) {
        // 获取方法签名和注解
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        MyLog myLog = AnnotationUtils.findAnnotation(method, MyLog.class);

        // 确定日志记录行为
        boolean shouldLog = myLog == null || myLog.enabled();
        boolean logRequestParams = shouldLog && (myLog == null || myLog.requestParam());
        boolean logResponseData = shouldLog && (myLog == null || myLog.responseData());
        boolean logErrorMsg = shouldLog && (myLog == null || myLog.ErrorMsg());
        int maxParamLength = myLog != null ? myLog.maxParamLength() : 2000;
        int maxResponseLength = myLog != null ? myLog.maxResponseLength() : 2000;
        int maxErrorLength = myLog != null ? myLog.maxErrorLength() : 2000;

        // 如果不记录日志，直接执行方法
        if (!shouldLog) {
            try {
                return joinPoint.proceed();
            } catch (Throwable throwable) {
                throw new BusinessException("环绕日志记录异常");
            }
        }

        // 创建日志对象
        OperationLog operationLog = new OperationLog();
        operationLog.setCreateTime(new Date());
        long startTime = System.currentTimeMillis();

        try {
            // 获取请求对象
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes != null ? attributes.getRequest() : null;

            // 填充基本信息
            if (request != null) {
                operationLog.setIpAddress(IpUtil.getIpAddress(request));
                operationLog.setRequestUrl(request.getRequestURI());
                operationLog.setRequestMethod(request.getMethod());
            }

            // 获取用户ID（如果有）
             operationLog.setUserAcct(StpUtil.isLogin()?StpUtil.getLoginIdAsString():null);

            // 方法信息
            operationLog.setClassMethod(signature.getDeclaringTypeName() + "#" + signature.getName());

            // 记录请求参数（如果启用）
            if (logRequestParams) {
                Object[] args = joinPoint.getArgs();
                if (args != null && args.length > 0) {
                    try {
                        String params = jacksonMapper.writeValueAsString(args);
                        operationLog.setRequestParams(truncateString(params, maxParamLength));
                    } catch (Exception e) {
                        operationLog.setRequestParams("参数序列化失败");
                    }
                }
            }

            // 执行目标方法
            Object result = joinPoint.proceed();
            // 记录成功状态
            operationLog.setStatus(1);
            // 记录响应数据（如果启用）
            if (logResponseData && result != null) {
                try {
                    String responseData = jacksonMapper.writeValueAsString(result);
                    operationLog.setResponseData(truncateString(responseData, maxResponseLength));

                } catch (Exception e) {
                    operationLog.setResponseData("responseData序列化失败");
                    operationLog.setErrorMsg(truncateString(e.getMessage(), maxErrorLength));
                }
            }
            return result;
        } catch (Throwable e) {
            // 记录失败状态
            operationLog.setStatus(0);
            // 记录错误信息（如果启用）
            if (logErrorMsg) {
                operationLog.setErrorMsg(truncateString(e.getMessage(), maxErrorLength));
            }
        } finally {
            // 计算耗时
            long endTime = System.currentTimeMillis();
            operationLog.setTimeCost(endTime - startTime);
            // 保存日志
            try {
                operationLogService.save(operationLog);
            } catch (Exception e) {
                log.error("操作日志保存失败", e);
            }
        }
        return null;
    }

    /**
     * 截断过长的字符串
     */
    private String truncateString(String value, int maxLength) {
        if (value == null) {
            return null;
        }
        return value.length() > maxLength ? value.substring(0, maxLength) : value;
    }

}